package com.tping.redis;

import java.util.*;

/**
 *
 * 最近最少使用策略，判断最近被使用的时间，距离目前最远的数据优先被淘汰（单维度：使用时间）
 *
 */
public class LRU<K, V> {

    private int capacity;

    private Map<K, Node> caches;

    public LRU(int size){
        this.capacity = size;
        caches = new LinkedHashMap<K, Node>(size);
    }

    public void put(K key, V value){
        Node node = caches.get(key);
        if( node == null ){
            // 判断容量是否满了
            if( caches.size() >= capacity ){
                K k = remoteLastNode();
                caches.remove(k);
            }

            node = new Node(key, value, System.nanoTime());
            caches.put(key, node);
        }else {
            node.value = value;
            node.time = System.nanoTime();
        }
        sort();
    }

    private K remoteLastNode() {
        Collection<Node> values = caches.values();
        Node min = Collections.min(values);
        return (K) min.key;
    }

    public V get(K key){
        Node node = caches.get(key);
        if( node == null ) return null;
        node.time = System.nanoTime();
        sort();
        return (V) node.value;
    }

    public Map<K, Node> getCaches(){
        return this.caches;
    }

    public int size(){
        return caches.size();
    }

    private void sort(){
        List<Map.Entry<K, Node>> list = new ArrayList<>(caches.entrySet());

        Collections.sort(list, new Comparator<Map.Entry<K, Node>>() {
            @Override
            public int compare(Map.Entry<K, Node> o1, Map.Entry<K, Node> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });

        caches.clear();
        for ( Map.Entry<K, Node> entry : list ){
            caches.put(entry.getKey(), entry.getValue());
        }
    }

    static class Node implements Comparable<Node>{

        Object key;
        Object value;

        long time;

        public Node(Object key, Object value, long time) {
            this.key = key;
            this.value = value;
            this.time = time;
        }

        @Override
        public int compareTo(Node o) {
            return Long.compare(this.time, o.time);
        }

        @Override
        public String toString() {
            return "Node{" +
                    "key=" + key +
                    ", value=" + value +
                    ", time=" + time +
                    '}';
        }
    }
}
